#pragma once
#include <windows.h>
#include <map>
#include <unordered_set>
#include <vector>
#include <set>
#include <iostream>
#include <cassert>
#include "../unit_pool/unit_allocator.hpp"

#ifdef _DEBUG
	//#define NOT_USE_STATIC_POOL
#endif

namespace umtl
{
	class block_partition;

	class memory_manager
	{
	public:
		//typedef boost::recursive_mutex Mutex;
		struct Mutex
		{
			Mutex() { InitializeCriticalSection(&ct); }
			~Mutex() { DeleteCriticalSection(&ct); }
			CRITICAL_SECTION ct;
		};
		//typedef boost::lock_guard<Mutex> Locker;
		typedef struct cLocker { 
			cLocker(Mutex & mutex) : mutex(mutex) { EnterCriticalSection(&(mutex.ct)); }
			~cLocker() { LeaveCriticalSection(&(mutex.ct)); }
			Mutex & mutex;
		private:
			cLocker( cLocker const& );
			void operator=( cLocker const& );
		} Locker;

		typedef std::vector< char* >										Chunks;
		typedef std::map< size_t, Chunks >									Blocks;

		void * alloc( size_t size );
		void free( void * p );
		void * alloc_array( size_t size ); 
		void free_array( void * p );
		void clear();

		size_t current_size();
		size_t alloc_total();
		size_t dealloc_total();

		static memory_manager & get();

		~memory_manager();
		
		inline size_t smallSize() { return _small_size; }

	private:
		Mutex bic_block_mutex;
		Mutex pool_size_mutex;
		Blocks blocks;
		size_t pool_size;
		size_t alloc_cumul;
		size_t dealloc_cumul;

		void * big_alloc( size_t size );
		void * new_big_mem( size_t size );
		void * search_big_mem( Blocks::iterator blockIter, size_t size );
		void big_free( void * p );

		void * small_alloc( size_t size );
		void small_free( void * p );

		static void outOfMem()
		{
			std::cerr<< "Unable to satisfy request for memory\n";
			//std::abort();
			throw std::bad_alloc();
		}

		memory_manager();

		memory_manager( memory_manager const & other );
		void operator=( memory_manager const & other );
		
		static int const _small_size = 5120;
	};
}